﻿using Bit.Art.Api.Controllers;
using Bit.Art.Core.Extensions;
using Bit.Art.Db;
using Bit.Art.Distributed;
using Bit.Art.Profiler;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace bit.art.test
{
#if UNITTEST
    [TestClass]
    public class 基本性能测试
    {
        protected static Memcache s_Memcache;
        private static ThreadPoolLogger s_ThreadPoolLogger;
        private static BandwidthLogger s_BandwidthLogger;

        [ClassInitialize]
        public static void 启动消息队列各个节点(TestContext context)
        {
            s_ThreadPoolLogger = new ThreadPoolLogger(TimeSpan.FromSeconds(2));
            s_BandwidthLogger = new BandwidthLogger(TimeSpan.FromSeconds(2));
            ApplicationLogging.LoggerFactory.AddConsole().AddDebug(LogLevel.Warning);
            s_Memcache = Memcache.Create("localhost");
            Bit.Art.Broker.Program.StartEngine();
            Bit.Art.Account.Program.StartEngine(s_Memcache);
            Bit.Art.Bookeeper.Program.StartEngine(s_Memcache);
            Bit.Art.Engine.Program.StartEngine(s_Memcache, 500, 100000);
            Bit.Art.MySqlDb.Program.StartEngine();
            // 等待1秒，确保所有节点都启动了
            Bit.Art.Broker.Program.s_autoResetEventForTest.WaitOne(1000);
            Bit.Art.Account.Program.s_autoResetEventForTest.WaitOne(1000);
            Bit.Art.Bookeeper.Program.s_autoResetEventForTest.WaitOne(1000);
            Bit.Art.Engine.Program.s_autoResetEventForTest.WaitOne(1000);
            Bit.Art.MySqlDb.Program.s_autoResetEventForTest.WaitOne(1000);
        }

        [TestInitialize]
        public async Task TestInitialize()
        {
            using (var db = new ExchangeDb())
            {
                DbInitializer.Intialize(db);
            }

            await s_Memcache.FlushDb();
            Bit.Art.Account.Program.LoadDataToCache(s_Memcache);
        }

        [TestCleanup]
        public void TestCleanup()
        {
            Bit.Art.Account.Program.s_autoResetEventForTest.Reset();
            Bit.Art.Bookeeper.Program.s_autoResetEventForTest.Reset();
            Bit.Art.Bookeeper.Program.s_filledAutoEventForTest.Reset();
            Bit.Art.Engine.Program.s_autoResetEventForTest.Reset();
            Bit.Art.MySqlDb.Program.s_autoResetEventForTest.Reset();

            using (var db = new ExchangeDb())
            {
                db.Database.ExecuteSqlCommand("DELETE FROM exchangedb.orders WHERE Id <> @p0; " +
                    "DELETE FROM exchangedb.accountbalances WHERE Id <> @p0", Guid.Empty);
            }
        }

        private async Task 执行性能测试(MarketController controller, string xls, string market, Action sleep)
        {
            using (var file = new FileStream(xls, FileMode.Open, FileAccess.Read))
            {
                var workbook = new HSSFWorkbook(file);
                var sheet = workbook.GetSheetAt(0);
                var rowCount = sheet.LastRowNum;
                int bidCallCount = 0, askCallCount = 0;
                for (int ridx = 1; ridx <= rowCount; ridx++)
                {
                    var row = sheet.GetRow(ridx);
                    decimal bidprice, bidvolume, askprice, askvolume;
                    if (row.GetCell(0) != null)
                    {
                        bidprice = Convert.ToDecimal(row.GetCell(0).NumericCellValue);
                        bidvolume = Convert.ToDecimal(row.GetCell(1).NumericCellValue);
                        controller._uid = TestConstants.DEFAULT_BID_USER_ID;
                        await controller.BuyLimit(market, bidprice, bidvolume);
                        bidCallCount++;
                    }

                    if (row.GetCell(2) != null)
                    {
                        askprice = Convert.ToDecimal(row.GetCell(2).NumericCellValue);
                        askvolume = Convert.ToDecimal(row.GetCell(3).NumericCellValue);
                        controller._uid = TestConstants.DEFAULT_ASK_USER_ID;
                        await controller.SellLimit(market, askprice, askvolume);
                        askCallCount++;
                    }
                }

                if (sleep != null)
                {
                    sleep();
                }
                else
                {
                    for (var i = 0; i < (bidCallCount + askCallCount); ++i)
                    {
                        Bit.Art.Bookeeper.Program.s_filledAutoEventForTest.WaitOne();
                    }
                }
            }

            controller._uid = TestConstants.DEFAULT_BID_USER_ID;
            var openOrdersResponse = await controller.GetOpenOrders(market);
            Assert.IsTrue(openOrdersResponse.Success);
            var actual = openOrdersResponse.Result;
            Assert.AreEqual(0, actual.Length);
            controller._uid = TestConstants.DEFAULT_ASK_USER_ID;
            openOrdersResponse = await controller.GetOpenOrders(market);
            Assert.IsTrue(openOrdersResponse.Success);
            actual = openOrdersResponse.Result;
            Assert.AreEqual(0, actual.Length);
        }
        
        [TestMethod]
        public async Task 限价单基本性能测试()
        {
            var controller = new MarketController();
            controller.SetMemcache(s_Memcache);
            var market = "BTC/USDT";

            await 执行性能测试(controller, "Data\\限价单基本性能测试数据简单.xls", market, () => System.Threading.Thread.Sleep(30 * 1000));

            using (var db = new ExchangeDb())
            {
                var conn = db.Database.GetDbConnection();
                conn.Open();
                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = "SELECT COUNT(*) FROM exchangedb.orders WHERE VolumeRemaining > 0";
                    var notfilled = (long)cmd.ExecuteScalar();
                    Assert.AreEqual(0, notfilled);
                }

                var balance = db.AccountBalances.Single(b => b.AccountId == TestConstants.DEFAULT_BID_USER_ID && b.Currency == "BTC");
                Assert.AreEqual(0, balance.Pending);
                // 扣除0.1%的手续费
                Assert.AreEqual(DbInitializer.INIT_BTC_BALANCE + 5.5m, balance.Balance);

                balance = db.AccountBalances.Single(b => b.AccountId == TestConstants.DEFAULT_BID_USER_ID && b.Currency == "USDT");
                Assert.AreEqual(0, balance.Pending);
                Assert.AreEqual(DbInitializer.INIT_USDT_BALANCE - 55008.3m * 1.001m, balance.Balance);

                balance = db.AccountBalances.Single(b => b.AccountId == TestConstants.DEFAULT_ASK_USER_ID && b.Currency == "BTC");
                Assert.AreEqual(0, balance.Pending);
                Assert.AreEqual(DbInitializer.INIT_BTC_BALANCE - 5.5m, balance.Balance);

                balance = db.AccountBalances.Single(b => b.AccountId == TestConstants.DEFAULT_ASK_USER_ID && b.Currency == "USDT");
                Assert.AreEqual(0, balance.Pending);
                Assert.AreEqual(DbInitializer.INIT_USDT_BALANCE + 55008.3m * 0.999m, balance.Balance);
            }
        }

        [TestMethod]
        public async Task 万余条数据性能测试()
        {
            var controller = new MarketController();
            controller.SetMemcache(s_Memcache);
            var market = "BTC/USDT";

            await 执行性能测试(controller, "Data\\限价单基本性能测试数据万余条.xls", market, () => System.Threading.Thread.Sleep(1 * 60 * 60 * 1000));

            using (var db = new ExchangeDb())
            {
                var conn = db.Database.GetDbConnection();
                conn.Open();
                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = "SELECT COUNT(*) FROM exchangedb.orders WHERE VolumeRemaining > 0";
                    var notfilled = (long)cmd.ExecuteScalar();
                    Assert.AreEqual(0, notfilled);
                }

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = "SELECT COUNT(*) FROM exchangedb.orders";
                    var notfilled = (long)cmd.ExecuteScalar();
                    // 所有的订单都应该正常下单
                    Assert.AreEqual(27720, notfilled);
                }

                var balance = db.AccountBalances.Single(b => b.AccountId == TestConstants.DEFAULT_BID_USER_ID && b.Currency == "BTC");
                Assert.AreEqual(0, balance.Pending);

                balance = db.AccountBalances.Single(b => b.AccountId == TestConstants.DEFAULT_BID_USER_ID && b.Currency == "USDT");
                Assert.AreEqual(0, balance.Pending);

                balance = db.AccountBalances.Single(b => b.AccountId == TestConstants.DEFAULT_ASK_USER_ID && b.Currency == "BTC");
                Assert.AreEqual(0, balance.Pending);

                balance = db.AccountBalances.Single(b => b.AccountId == TestConstants.DEFAULT_ASK_USER_ID && b.Currency == "USDT");
                Assert.AreEqual(0, balance.Pending);
            }
        }
    }
#endif
}
